import os
import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from iou3d_nms_utils import *
import seaborn as sns
import re

def voc_ap(rec, prec, use_07_metric=False):
    """ ap = voc_ap(rec, prec, [use_07_metric])
    Compute VOC AP given precision and recall.
    If use_07_metric is true, uses the
    VOC 07 11 point method (default:False).
    """
    # 针对2007年VOC，使用的11个点计算AP，现在不使用
    if use_07_metric:
        # 11 point metric
        ap = 0.
        for t in np.arange(0., 1.1, 0.1):
            if np.sum(rec >= t) == 0:
                p = 0
            else:
                p = np.max(prec[rec >= t])
            ap = ap + p / 11.
    else:
        # correct AP calculation
        # first append sentinel values at the end
        mrec = np.concatenate(([0.], rec, [1.]))  # [0.  0.0666, 0.1333, 0.4   , 0.4666,  1.]
        mpre = np.concatenate(([0.], prec, [0.]))  # [0.  1.,     0.6666, 0.4285, 0.3043,  0.]

        # compute the precision envelope
        # 计算出precision的各个断点(折线点)
        for i in range(mpre.size - 1, 0, -1):
            mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])  # [1.     1.     0.6666 0.4285 0.3043 0.    ]

        # to calculate area under PR curve, look for points
        # where X axis (recall) changes value
        i = np.where(mrec[1:] != mrec[:-1])[0]  # precision前后两个值不一样的点
        # print(mrec[1:], mrec[:-1])
        # print(i)  # [0, 1, 3, 4, 5]

        # AP= AP1 + AP2+ AP3+ AP4
        ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
    return ap

def get_confusion_matrix(gt_labels,pr_labels):
    # gt_labels=object_kpi1['gt_type']
    # pr_labels=object_kpi1['pr_type']
    labels = np.union1d(gt_labels.unique(), pr_labels.unique())
    matrixs=np.zeros([labels.__len__(),labels.__len__()])
    for i in range(labels.__len__()):
        pr_labels_temp=pr_labels[gt_labels==labels[i]]
        for j in range(labels.__len__()):
            matrixs[i,j]=sum(pr_labels_temp==labels[j])
    C=pd.DataFrame(matrixs)
    C.columns=labels
    C=C.set_index(labels)
    return C

def plot_confusion_matrix(object_kpi1,output_path, image_name):
    confusion_matrix=get_confusion_matrix(object_kpi1['gt_type'],object_kpi1['pr_type'])
    confusion_matrix_temp=(confusion_matrix.T/confusion_matrix.sum(1).T).T
    plt.figure(figsize=(7.5, 6.3))
    ax = sns.heatmap(confusion_matrix, xticklabels=confusion_matrix.corr().columns,
                     yticklabels=confusion_matrix.corr().columns, cmap='cool',
                     linewidths=6, annot=True)
    plt.ylabel('Truth label')
    plt.xlabel('Predict label')
    plt.title(image_name)
    plt.savefig(os.path.join(output_path, image_name))
    plt.close()
    return confusion_matrix
    # plt.tight_layout()
    # plt.show()

def get_radar_AP(object_kpi1,iou_valve,image_name):
    data_temp=object_kpi1.sort_values(by='score',inplace=False,ascending=False)
    TP=(data_temp['iou_bev']>iou_valve)&(data_temp['gt_index']&data_temp['pr_index'])
    # FP=~TP
    # acc_FP=FP.cumsum().to_numpy()
    acc_TP=TP.cumsum().to_numpy()
    pr_num=data_temp['pr_index'].cumsum().to_numpy()
    pr_num[pr_num==0]=1
    gt_num=sum(data_temp['gt_index'])
    if gt_num==0:
        gt_num=1
    precise=acc_TP/pr_num
    recall=acc_TP/gt_num
    AP = voc_ap(recall, precise)
    # print(AP,precise.to_list()[-1],recall.to_list()[-1])
    if isinstance(AP,float)==False:
        return AP
    plt.plot(recall,precise)
    plt.xlabel('recall')
    plt.ylabel('precise')
    plt.title('P-R curve')
    plt.fill_between(recall, precise, alpha=0.5, where=precise>0,label='precise')
    # plt.axis([0,max(recall),min(precise),1])
    plt.savefig(image_name)
    plt.close()
    sum_data = precise+recall
    # print(sum_data)
    # precise = precise[sum_data == max(sum_data)].tolist()[0]
    # recall = recall[sum_data == max(sum_data)].tolist()[0]
    pr_num=sum(data_temp['pr_index'])
    if pr_num==0:
        pr_num=1
    precise = sum(TP)/pr_num
    recall = sum(TP)/gt_num
    score = data_temp['score'].to_numpy()[sum_data == max(sum_data)][0]
    return AP,precise,recall,score

def get_AP(object_kpi1,iou_valve,image_name):
    data_temp=object_kpi1.sort_values(by='score',inplace=False,ascending=False)
    TP=(data_temp['iou_bev'].to_numpy()>iou_valve)&(data_temp['gt_index']&data_temp['pr_index'])
    # FP=~TP
    # acc_FP=FP.cumsum().to_numpy()
    acc_TP=TP.cumsum().to_numpy()
    pr_num=data_temp['pr_index'].cumsum().to_numpy()
    pr_num[pr_num==0]=1
    gt_num=sum(data_temp['gt_index'])
    if gt_num==0:
        gt_num=1
    precise=acc_TP/pr_num
    recall=acc_TP/gt_num
    AP = voc_ap(recall, precise)
    # print(AP,precise.to_list()[-1],recall.to_list()[-1])
    if isinstance(AP,float)==False:
        return AP
    plt.plot(recall,precise)
    plt.xlabel('recall')
    plt.ylabel('precise')
    plt.title('P-R curve')
    plt.fill_between(recall, precise, alpha=0.5, where=precise>0,label='precise')
    # plt.axis([0,max(recall),min(precise),1])
    plt.savefig(image_name)
    plt.close()
    sum_data = precise+recall
    # precise = precise[sum_data == max(sum_data)].tolist()[0]
    # recall = recall[sum_data == max(sum_data)].tolist()[0]
    # score = data_temp['score'].to_numpy()[sum_data == max(sum_data)][0]
    precise = precise[-1]
    recall = recall[-1]
    # print(data_temp['score'])
    score = list(data_temp['score'])[-1]
    print(score)
    return AP,precise,recall,score

def get_json_data(label_file):
    # 读取预测数据
    with open(label_file, 'r') as f:
        data_label = json.load(f)
    name = [i['obj_type'].lower() for i in data_label]
    id = [i['obj_id'] for i in data_label]
    xyz = np.array(
        [[i['psr']['position']['x'], i['psr']['position']['y'], i['psr']['position']['z']] for i in data_label])
    lwh = np.array([[i['psr']['scale']['x'], i['psr']['scale']['y'], i['psr']['scale']['z']] for i in data_label])
    r = np.array([[i['psr']['rotation']['z']] for i in data_label])
    box = np.concatenate([xyz, lwh, r], axis=-1)
    box[:, 2] = box[:, 2] - 3.15
    velocity = np.zeros([name.__len__(), 5]).tolist()
    acceleration = np.zeros([name.__len__(), 5]).tolist()
    if 'gt' not in label_file:
        score = [i['score'] for i in data_label]
        return name, id, box, velocity, acceleration, score
    else:
        return name, id, box, velocity, acceleration

def get_jsons_data(label_file):
    # 读取检测数据
    with open(label_file, 'r') as f:
        data_label = json.load(f)
    name = data_label['annos']['name']
    id = data_label['annos']['track_id']
    box = data_label['annos']['gt_boxes_3d']
    box = np.array(box)
    velocity = data_label['annos']['velocity']
    acceleration = data_label['annos']['acceleration']
    if 'gt' not in label_file:
        score = data_label['annos']['confidence']
        return name, id, box, velocity, acceleration, score
    else:
        return name, id, box, velocity, acceleration

def get_target_match_data(pr_data_path,
                          pr_data_type,
                          gt_data_path,
                          gt_data_type,
                          scene,
                          iou_valve):

    pr_file_list = os.listdir(pr_data_path)
    gt_file_list = os.listdir(gt_data_path)
    file_list = np.intersect1d(pr_file_list,gt_file_list)
    file_list = sorted(file_list)
    object_kpi=[]
    for file_name in file_list:
        # 设置文件路径
        pr_label_json = os.path.join(pr_data_path,file_name)
        gt_label_json = os.path.join(gt_data_path,file_name)

        if pr_data_type=='json':
            name_pr, id_pr, box_pr, velocity_pr, acceleration_pr, score = get_json_data(pr_label_json)
        else:
            name_pr, id_pr, box_pr, velocity_pr, acceleration_pr, score = get_jsons_data(pr_label_json)
        if gt_data_type == 'json':
            name_gt, id_gt, box_gt, velocity_gt, acceleration_gt = get_json_data(gt_label_json)
        else:
            name_gt, id_gt, box_gt, velocity_gt, acceleration_gt = get_jsons_data(gt_label_json)

        if box_pr.__len__()>0 and box_gt.__len__()>0:

            # # 计算iou3d
            # boxes_a = torch.from_numpy(box_pr).cuda().float()
            # boxes_b = torch.from_numpy(box_gt).cuda().float()

            # iou=boxes_iou3d_gpu(boxes_a,boxes_b)


            box_gt_temp = box_gt.copy()
            box_pr_temp = box_pr.copy()

            # 计算iou3d_bev
            box_pr[:, 2] = 0
            box_pr[:, 5] = 1
            box_gt[:, 2] = 0
            box_gt[:, 5] = 1

            # boxes_a = torch.from_numpy(box_pr).cuda().float()
            # boxes_b = torch.from_numpy(box_gt).cuda().float()

            # iou_bev=boxes_iou3d_gpu(boxes_a,boxes_b)
            iou_bev = boxes_bev_iou_cpu(box_pr, box_gt)
            iou = boxes_bev_iou_cpu(box_pr_temp, box_gt_temp)

        elif box_pr.__len__()<=0:
            pr_indexs=[]
            gt_indexs=[]
            iou_bev=np.array([])
            box_gt_temp = box_gt.copy()
            box_pr_temp = box_pr.copy()
            iou=np.array([])
        elif box_gt.__len__()<=0:
            pr_indexs=[]
            gt_indexs=[]
            iou_bev=np.array([])
            box_gt_temp = box_gt.copy()
            box_pr_temp = box_pr.copy()
            iou=np.array([])

        if box_gt_temp.__len__()>0:
            # 角度修正到（-90~90度）
            gt_phi = box_gt_temp[:,6] * 180 / np.pi
            gt_phi = gt_phi % 360
            gt_phi[gt_phi > 270] = gt_phi[gt_phi > 270] - 360
            gt_phi[(gt_phi > 90) & (gt_phi <= 270)] = gt_phi[(gt_phi > 90) & (gt_phi <= 270)] - 180
            box_gt_temp[:,6] = gt_phi
            sin_gt_phi = np.sin(box_gt_temp[:, 6] / 180 * np.pi)
            cos_gt_phi = np.cos(box_gt_temp[:, 6] / 180 * np.pi)
            box_gt_temp[:,0:2] = np.array([cos_gt_phi, sin_gt_phi]).T * (-1 / 2) * np.array(
                [box_gt_temp[:,3], box_gt_temp[:,3]]).T + box_gt_temp[:,0:2]

        if box_pr_temp.__len__()>0:
            pr_phi = box_pr_temp[:,6] * 180 / np.pi
            pr_phi = pr_phi % 360
            pr_phi[pr_phi > 270] = pr_phi[pr_phi > 270] - 360
            pr_phi[(pr_phi > 90) & (pr_phi <= 270)] = pr_phi[(pr_phi > 90) & (pr_phi <= 270)] - 180
            box_pr_temp[:,6] = pr_phi

            # 车辆位置从几何中点变换到车位中点
            sin_pr_phi = np.sin(box_pr_temp[:,6] / 180 * np.pi)
            cos_pr_phi = np.cos(box_pr_temp[:,6] / 180 * np.pi)

            box_pr_temp[:,0:2] = np.array([cos_pr_phi, sin_pr_phi]).T * (-1 / 2) * np.array(
                [box_pr_temp[:,3], box_pr_temp[:,3]]).T + box_pr_temp[:,0:2]

        # 目标匹配
        # [pr_index,gt_index]=np.where(iou_bev.cpu().numpy() > 0)

        if iou_bev.__len__()>0:
            iou_temp = iou_bev > iou_valve
            a = np.sum(iou_temp, axis=1)
            rows=np.where(a>1)[0]
            for row_id in range(rows.__len__()):
                row=rows[row_id]
                colums=np.where(iou_bev[row,:] > iou_valve)[0]
                rows_temp=np.repeat(row,colums.__len__())
                distance_temp=(np.sum((box_gt_temp[colums,0:1]-box_pr_temp[rows_temp,0:1])**2,axis=1))**(1/2)
                iou_bev[rows_temp[distance_temp>np.min(distance_temp)],colums[distance_temp>np.min(distance_temp)]]=0

            b = np.sum(iou_temp, axis=0)
            colums=np.where(b>1)[0]
            for colum_id in range(colums.__len__()):
                colum=colums[colum_id]
                rows=np.where(iou_bev[:,colum] > iou_valve)[0]
                colums_temp=np.repeat(colum,rows.__len__())
                distance_temp=(np.sum((box_gt_temp[colums_temp,0:1]-box_pr_temp[rows,0:1])**2,axis=1))**(1/2)
                iou_bev[rows[distance_temp>np.min(distance_temp)],colums_temp[distance_temp>np.min(distance_temp)]]=0

            [pr_indexs, gt_indexs] = np.where(iou_bev > iou_valve)

        for i in range(iou_bev.__len__()):
            if np.where(iou_bev[i] > 0).__len__()>1:
                print(pr_label_json)
        for i in range(iou_bev.T.__len__()):
            if np.where(iou_bev[:,i] > 0).__len__()>1:
                print(gt_label_json)

        for i in range(pr_indexs.__len__()):
            pr_index = pr_indexs[i]
            gt_index = gt_indexs[i]
            # output_temp=[max(iou.cpu().numpy()[i]),max(iou_bev.cpu().numpy()[i]),score[i],name_pr[i]]
            output_temp = [iou[pr_index,gt_index], iou_bev[pr_index,gt_index], score[pr_index], name_pr[pr_index], name_gt[gt_index],id_pr[pr_index], id_gt[gt_index]]

            output_temp += list(box_pr_temp[pr_index])
            output_temp += list(velocity_pr[pr_index])[0:2]
            output_temp += list(acceleration_pr[pr_index])[0:2]

            output_temp += list(box_gt_temp[gt_index])
            if isinstance(velocity_gt[gt_index],list):
                output_temp += list(velocity_gt[gt_index])[0:2]
            else:
                output_temp += [np.nan,np.nan]
            if isinstance(velocity_gt[gt_index], list):
                output_temp += list(acceleration_gt[gt_index])[0:2]
            else:
                output_temp += [np.nan, np.nan]

            output_temp += [file_name,scene]
            object_kpi.append(output_temp)

        # 误识目标
        tf_pr = np.setdiff1d(np.linspace(0,name_pr.__len__()-1,name_pr.__len__()).astype(int),pr_indexs)
        for i in range(tf_pr.__len__()):
            pr_index = tf_pr[i]
            # output_temp=[max(iou.cpu().numpy()[i]),max(iou_bev.cpu().numpy()[i]),score[i],name_pr[i]]
            output_temp = [0, 0, score[pr_index], name_pr[pr_index], 'NULL',id_pr[pr_index], -1]
            output_temp += list(box_pr_temp[pr_index])
            output_temp += list(velocity_pr[pr_index])[0:2]
            output_temp += list(acceleration_pr[pr_index])[0:2]
            output_temp += list(box_pr_temp[pr_index])
            output_temp += list(velocity_pr[pr_index])[0:2]
            output_temp += list(acceleration_pr[pr_index])[0:2]
            output_temp += [file_name,scene]
            object_kpi.append(output_temp)
        # 漏识目标
        tf_gt = np.setdiff1d(np.linspace(0, name_gt.__len__() - 1, name_gt.__len__()).astype(int), gt_indexs)
        for i in range(tf_gt.__len__()):
            gt_index = tf_gt[i]
            # output_temp=[max(iou.cpu().numpy()[i]),max(iou_bev.cpu().numpy()[i]),score[i],name_pr[i]]
            output_temp = [0, 0, 0, 'NULL', name_gt[gt_index], -1, id_gt[gt_index]]
            output_temp += list(box_gt_temp[gt_index])
            if isinstance(velocity_gt[gt_index],list):
                output_temp += list(velocity_gt[gt_index])[0:2]
            else:
                output_temp += [np.nan,np.nan]
            if isinstance(velocity_gt[gt_index], list):
                output_temp += list(acceleration_gt[gt_index])[0:2]
            else:
                output_temp += [np.nan, np.nan]
            output_temp += list(box_gt_temp[gt_index])
            if isinstance(velocity_gt[gt_index],list):
                output_temp += list(velocity_gt[gt_index])[0:2]
            else:
                output_temp += [np.nan,np.nan]
            if isinstance(velocity_gt[gt_index], list):
                output_temp += list(acceleration_gt[gt_index])[0:2]
            else:
                output_temp += [np.nan, np.nan]
            output_temp += [file_name,scene]
            object_kpi.append(output_temp)

    return object_kpi

def data_process(object_kpi_pd):
    # # z轴变换
    # object_kpi_pd['gt_z']=object_kpi_pd['gt_z']+3.15
    # # 角度修正到（-90~90度）
    # gt_phi=object_kpi_pd['gt_phi'].to_numpy()*180/np.pi
    # gt_phi=gt_phi%360
    # gt_phi[gt_phi>270]=gt_phi[gt_phi>270]-360
    # gt_phi[(gt_phi > 90)&(gt_phi <= 270)] = gt_phi[(gt_phi > 90)&(gt_phi <= 270)] - 180
    # object_kpi_pd['gt_phi']=gt_phi
    #
    # pr_phi=object_kpi_pd['pr_phi'].to_numpy()*180/np.pi
    # pr_phi=pr_phi%360
    # pr_phi[pr_phi>270]=pr_phi[pr_phi>270]-360
    # pr_phi[(pr_phi > 90)&(pr_phi <= 270)] = pr_phi[(pr_phi > 90)&(pr_phi <= 270)] - 180
    # object_kpi_pd['gt_phi']=pr_phi
    #
    # 车辆位置从几何中点变换到车位中点
    # sin_pr_phi = np.sin(object_kpi_pd['pr_phi']/180*np.pi)
    # cos_pr_phi = np.cos(object_kpi_pd['pr_phi']/180*np.pi)
    # sin_gt_phi = np.sin(object_kpi_pd['gt_phi']/180*np.pi)
    # cos_gt_phi = np.cos(object_kpi_pd['gt_phi']/180*np.pi)

    #左边车辆
    # datas_temp=object_kpi_pd[object_kpi_pd['gt_y']<=-4].copy()
    # sin_pr_phi = np.sin(datas_temp['pr_phi']/180*np.pi)
    # cos_pr_phi = np.cos(datas_temp['pr_phi']/180*np.pi)
    # sin_gt_phi = np.sin(datas_temp['gt_phi']/180*np.pi)
    # cos_gt_phi = np.cos(datas_temp['gt_phi']/180*np.pi)
    # datas_temp.loc[:, ('pr_x', 'pr_y')] = np.array([-1*sin_pr_phi, cos_pr_phi]).T * (1 / 2) * np.array(
    #     [datas_temp['pr_w'], datas_temp['pr_w']]).T + datas_temp.loc[:, ('pr_x', 'pr_y')].to_numpy()
    # datas_temp.loc[:, ('gt_x', 'gt_y')] = np.array([-1*sin_gt_phi, cos_gt_phi]).T * (1 / 2) * np.array(
    #     [datas_temp['gt_w'], datas_temp['gt_w']]).T + datas_temp.loc[:, ('gt_x', 'gt_y')].to_numpy()
    # object_kpi_pd[object_kpi_pd['gt_y']<=-4]=datas_temp
    # 右边车辆
    # datas_temp=object_kpi_pd[object_kpi_pd['gt_y']>=4].copy()
    # sin_pr_phi = np.sin(datas_temp['pr_phi']/180*np.pi)
    # cos_pr_phi = np.cos(datas_temp['pr_phi']/180*np.pi)
    # sin_gt_phi = np.sin(datas_temp['gt_phi']/180*np.pi)
    # cos_gt_phi = np.cos(datas_temp['gt_phi']/180*np.pi)
    # datas_temp.loc[:, ('pr_x', 'pr_y')] = np.array([sin_pr_phi, cos_pr_phi]).T * (1 / 2) * np.array(
    #     [datas_temp['pr_w'], datas_temp['pr_w']]).T + datas_temp.loc[:, ('pr_x', 'pr_y')].to_numpy()
    # datas_temp.loc[:, ('gt_x', 'gt_y')] = np.array([sin_gt_phi, cos_gt_phi]).T * (1 / 2) * np.array(
    #     [datas_temp['gt_w'], datas_temp['gt_w']]).T + datas_temp.loc[:, ('gt_x', 'gt_y')].to_numpy()
    # object_kpi_pd[object_kpi_pd['gt_y']<=-4]=datas_temp

    distance = (object_kpi_pd['gt_x'] ** 2 + object_kpi_pd['gt_y'] ** 2) ** (1 / 2)
    object_kpi_pd['distance'] = distance
    object_kpi_pd['distance'].describe()

    tan_x_y=object_kpi_pd['gt_x']/ np.abs(object_kpi_pd['gt_y'])
    object_kpi_pd = object_kpi_pd[tan_x_y>np.tan(33/180*np.pi)]
    # 去掉
    # object_kpi_pd=object_kpi_pd[object_kpi_pd['gt_type']!='other']
    # object_kpi_pd = object_kpi_pd[object_kpi_pd['distance'] <= 300]
    return object_kpi_pd

def compute_evaluation_MOT(object_kpi_pd,config):
    MOT = {}

    for type in config['out_classes'].keys():
        indexs = object_kpi_pd['gt_type'] == config['out_classes'][type][0]
        for i in config['out_classes'][type]:
            indexs = indexs | (object_kpi_pd['gt_type'] == i)
        object_kpi_pd1 = object_kpi_pd[indexs]
        for scene in config['scene'].keys():
            object_kpi_pd_temp = object_kpi_pd1[(object_kpi_pd1['scene']==scene)].copy()
            FP_num = np.sum(object_kpi_pd_temp['gt_id']==-1)
            object_kpi_pd_temp = object_kpi_pd_temp[object_kpi_pd_temp['gt_id']!=-1]
            gt_num=object_kpi_pd_temp.__len__()
            gt_obj_num=object_kpi_pd_temp['gt_id'].unique().__len__()
            MT_num = 0
            ML_num = 0
            FN_num = 0
            ID_switch_num = 0
            FM_num = 0
            obj_num_valid = 0
            for gt_id in object_kpi_pd_temp['gt_id'].unique():
                data_temp = object_kpi_pd_temp[object_kpi_pd_temp['gt_id'] == gt_id]
                ids=data_temp['pr_id'].to_numpy().copy()
                FN=np.sum(ids==-1)
                ID_switch = np.sum(np.diff(ids[ids!=-1])!=0)
                ids_temp=ids.copy()
                ids_temp[ids_temp != -1] = 0
                FM = np.sum(np.diff(ids_temp)!=0)//2

                if ids.__len__()>10:
                    obj_num_valid += 1
                    if FN/ids.__len__()>0.8:
                        ML_num += 1
                    elif FN/ids.__len__()<0.2:
                        MT_num += 1

                FM_num += FM
                ID_switch_num += ID_switch
                FN_num += FN
            if type not in MOT:
                MOT[type]={}
            if scene not in MOT[type]:
                MOT[type][scene]={}
            data_temp = object_kpi_pd_temp[object_kpi_pd_temp['pr_id'] != -1]
            MOT[type][scene]['distance']=np.sum(((data_temp['gt_x']-data_temp['pr_x'])**2+(data_temp['gt_y']-data_temp['pr_y'])**2+(data_temp['gt_z']-data_temp['pr_z'])**2)**(1/2))
            MOT[type][scene]['match_num']=data_temp.__len__()
            MOT[type][scene]['FN'] = FN_num
            MOT[type][scene]['FP'] = FP_num
            MOT[type][scene]['FM'] = FM_num
            MOT[type][scene]['ID_switch'] = ID_switch_num
            MOT[type][scene]['gt_num'] = gt_num
            MOT[type][scene]['obj_num'] = gt_obj_num # 被跟踪的目标数（在多帧出现）

            MOT[type][scene]['obj_num_valid'] = obj_num_valid  # 被跟踪的有效目标数（在多帧出现）
            MOT[type][scene]['ML'] = ML_num
            MOT[type][scene]['MT'] = MT_num
        scene = 'all_scene'
        MOT[type][scene]=pd.DataFrame(MOT[type]).T.sum().to_dict()

    MOTs={}
    for type in config['out_classes'].keys():
        scene_list = list(config['scene'].keys())
        for j in range(scene_list.__len__() + 1):
            # 遍历场景
            if j == 0:
                scene = 'all_scene'
            else:
                scene = scene_list[j - 1]
            data = MOT[type][scene]
            if type not in MOTs:
                MOTs[type] = {}
            if scene not in MOTs[type]:
                MOTs[type][scene] = {}
            if data['gt_num'] == 0:
                MOTs[type][scene]['MOTA'] = 1
            else:
                MOTs[type][scene]['MOTA'] = 1 - (data['FN'] +data['FP'] + data['ID_switch']) / data['gt_num']
            if data['match_num'] == 0:
                MOTs[type][scene]['MOTP'] = 0
            else:
                MOTs[type][scene]['MOTP'] = data['distance'] / data['match_num']
            if data['obj_num_valid']==0:
                MOTs[type][scene]['MT'] = 0
                MOTs[type][scene]['ML'] = 0
            else:
                MOTs[type][scene]['MT'] = data['MT']/data['obj_num_valid']
                MOTs[type][scene]['ML'] = data['ML']/data['obj_num_valid']
            if data['gt_num']==0:
                MOTs[type][scene]['FM'] = 0
            else:
                MOTs[type][scene]['FM'] = data['FM'] #/data['gt_num']
            MOTs[type][scene]['obj_num'] = data['obj_num']
    return MOTs

def compute_evaluation_index(object_kpi_pd,config):
    EEOR={}
    APS_data= {}
    scene_list = list(config['scene'].keys())
    indexs_type=config['indexs_type']
    distance_str = config['distance']
    output_path=config['output_path']
    iou_valve=config['iou_valve']
    for j in range(scene_list.__len__()+1):
        # 遍历场景
        if j==0:
            scene = 'all_scene'
            object_kpi1 = object_kpi_pd
        else:
            scene = scene_list[j-1]
            object_kpi1 = object_kpi_pd[object_kpi_pd['scene']==scene]

        # 遍历距离
        for distance_id in range(distance_str.__len__()):
            distance = distance_str[distance_id]
            if distance_id==0:
                datas_temp = object_kpi1.copy()
            elif distance_id==1:
                datas_temp = object_kpi1.loc[object_kpi1['distance'] <= 50].copy()
            elif distance_id==2:
                datas_temp = object_kpi1.loc[(50 < object_kpi1['distance']) & (object_kpi1['distance'] <= 100)].copy()
            elif distance_id==3:
                datas_temp = object_kpi1.loc[(100 < object_kpi1['distance']) & (object_kpi1['distance'] <= 200)].copy()
            elif distance_id==4:
                datas_temp = object_kpi1.loc[(200 < object_kpi1['distance']) & (object_kpi1['distance'] <= 300)].copy()
            elif distance_id==5:
                datas_temp = object_kpi1.loc[object_kpi1['distance'] > 300].copy()
            if datas_temp.__len__()==0:
                # print(pkg_list_, distance_str[distance_id])
                continue
            classes=datas_temp['gt_type'].unique()
            # 遍历类别
            for type_id in range(classes.__len__()+2):
                if type_id==0:
                    type = 'all_class'
                    gt_index=(datas_temp['gt_type'] != 'NULL')
                    pr_index=(datas_temp['pr_type'] != 'NULL')
                    datas_temp['gt_index']=gt_index
                    datas_temp['pr_index'] = pr_index
                    data_temp = datas_temp.loc[gt_index|pr_index]
                elif type_id==1:
                    type = 'vehicle'
                    gt_index=((datas_temp['gt_type'] == 'car')|(datas_temp['gt_type'] == 'truck')|(datas_temp['gt_type'] == 'trailer'))
                    pr_index=((datas_temp['pr_type'] == 'car')|(datas_temp['pr_type'] == 'truck')|(datas_temp['pr_type'] == 'trailer'))
                    datas_temp['gt_index'] = gt_index
                    datas_temp['pr_index'] = pr_index
                    data_temp = datas_temp.loc[gt_index | pr_index]
                else:
                    type=classes[type_id-2]
                    if type=='NULL':
                        continue
                    gt_index=(datas_temp['gt_type'] == type)
                    pr_index=(datas_temp['pr_type'] == type)
                    datas_temp['gt_index']=gt_index
                    datas_temp['pr_index'] = pr_index
                    data_temp = datas_temp.loc[gt_index|pr_index]

                if data_temp[(data_temp['gt_type'] != 'NULL') & (data_temp['pr_type'] != 'NULL')].__len__() == 0:
                    continue

                P_R_name = os.path.join(output_path,'P_R_curve',scene + distance+ '_' + type + '.jpg')
                AP,precise,recall,score=get_AP(data_temp,iou_valve,P_R_name)
                APS= {
                    'AP':AP,
                    'precise':precise,
                    'recall':recall,
                    'score':score
                }
                # print('APS',AP,precise,recall,score)
                for index_type in config['APS']:
                    if type not in APS_data:
                        APS_data[type] = {}
                    if scene not in APS_data[type]:
                        APS_data[type][scene] = {}
                    if index_type not in APS_data[type][scene]:
                        APS_data[type][scene][index_type] = {}
                    APS_data[type][scene][index_type][distance]=APS[index_type]

                data_temp=data_temp[(data_temp['gt_type']!='NULL')&(data_temp['pr_type']!='NULL')]
                if data_temp.__len__()==0:
                    continue

                for index_type in indexs_type:
                    delta = data_temp['gt_'+index_type]-data_temp['pr_'+index_type]
                    delta = np.abs(delta)
                    for describe in config['describe']:
                        if type not in EEOR:
                            EEOR[type] = {}
                        if scene not in EEOR[type]:
                            EEOR[type][scene] = {}
                        if index_type not in EEOR[type][scene]:
                            EEOR[type][scene][index_type] = {}

                        if isinstance(describe,str):
                            if describe not in EEOR[type][scene][index_type]:
                                EEOR[type][scene][index_type][describe] = {}
                            if describe=='MAE':
                                delta_temp = delta.copy()
                                delta_temp[(delta_temp < delta_temp.quantile(0.05)) | (delta_temp > delta_temp.quantile(0.95))] = np.nan
                                delta_temp = delta_temp.dropna()
                                # EEOR[type][scene][index_type][describe][distance] = (delta_temp ** 2).mean() ** (1 / 2)
                                EEOR[type][scene][index_type][describe][distance] = delta_temp.abs().mean()
                            elif describe=='data_num':
                                EEOR[type][scene][index_type][describe][distance] = int(delta.__len__())
                            elif describe == 'MAX':
                                EEOR[type][scene][index_type][describe][distance] = delta.describe()['max']
                        elif isinstance(describe,list):
                            for describe_ in describe:
                                if describe_ not in EEOR[type][scene][index_type]:
                                    EEOR[type][scene][index_type][describe_] = {}
                                quantile = re.findall(r"\d+\.?\d*",describe_)[0]
                                EEOR[type][scene][index_type][describe_][distance] = delta.quantile(float(quantile)/100)

    return APS_data,EEOR

def plot_evaluation_index(object_kpi_pd,config):
    Bar_table={}
    scene_list = list(config['scene'].keys())
    # output_path = config['output_path']
    distance_str = config['distance']
    report_txt={'data_describe':[],'confusion':[]}
    for j in range(scene_list.__len__()+1):
        # 遍历场景
        if j==0:
            scene = 'all_scene'
            object_kpi1 = object_kpi_pd
        else:
            scene = scene_list[j-1]
            object_kpi1 = object_kpi_pd[object_kpi_pd['scene']==scene]
        if scene not in Bar_table:
            Bar_table[scene]={}
        types=object_kpi1['gt_type'].unique()
        for type in types:
            Bar_table[scene][type]=sum(object_kpi1['gt_type']==type)
    data_table = pd.DataFrame(Bar_table)
    scenes=['all_scene']+scene_list
    bottom_list=np.zeros(scenes.__len__())
    # colors={'NULL':'orange', 'car':'#ff7f0e', 'truck':'red', 'other':'green'}
    colors = {'car': '#1f77b4', 'truck': '#ff7f0e', 'NULL': '#d62728', 'other': '#7f7f7f'}
    plt.figure()
    for type in colors:
        if type in data_table.index:
            plt.bar(scenes, data_table.loc[type].to_numpy(), 0.5, label = type, bottom=bottom_list,color=colors[type])
            bottom_list += data_table.loc[type]
    plt.xticks(rotation=-10)
    plt.title('hist_of_class')
    plt.legend()
    image_path = os.path.join('data_describe', 'hist_of_class.jpg')
    print(image_path)
    plt.savefig(os.path.join(config['data_describe'], 'hist_of_class.jpg'))
    plt.close()
    report_txt['data_describe'] += ['***各类别直方图***  \n\n']
    report_txt['data_describe'] += ['![图1](' + image_path + ' "Magic Gardens")  \n']
    for j in range(scene_list.__len__()+1):
        # 遍历场景
        if j==0:
            pkg_list_ = 'all_scene'
            object_kpi1 = object_kpi_pd
        else:
            pkg_list_ = scene_list[j-1]
            object_kpi1 = object_kpi_pd[object_kpi_pd['scene']==pkg_list_]
        datas_temp = object_kpi1[(object_kpi1['gt_type'] != 'NULL') & (object_kpi1['pr_type'] != 'NULL')]
        plt.figure()
        plt.hist(datas_temp['iou_bev'], bins=50)
        plt.title(pkg_list_+'_hist_of_iou_bev')
        image_path = os.path.join('data_describe', pkg_list_+'_hist_of_iou_bev.jpg')
        plt.savefig(os.path.join(config['data_describe'], pkg_list_+'_hist_of_iou_bev.jpg'))
        plt.close()
        report_txt['data_describe'] += ['***场景' + pkg_list_ + ' iou bev直方图***  \n\n']
        report_txt['data_describe'] += ['![图1](' + image_path + ' "Magic Gardens")  \n']
        # 遍历距离
        for i in range(distance_str.__len__()):
            if i==0:
                datas_temp = object_kpi1
            elif i==1:
                datas_temp = object_kpi1.loc[object_kpi1['distance'] <= 50]
            elif i==2:
                datas_temp = object_kpi1.loc[(50 < object_kpi1['distance']) & (object_kpi1['distance'] <= 100)]
            elif i==3:
                datas_temp = object_kpi1.loc[(100 < object_kpi1['distance']) & (object_kpi1['distance'] <= 200)]
            elif i==4:
                datas_temp = object_kpi1.loc[(200 < object_kpi1['distance']) & (object_kpi1['distance'] <= 300)]
            elif i==5:
                datas_temp = object_kpi1.loc[object_kpi1['distance'] > 300]
            if datas_temp.__len__()==0:
                # print(pkg_list_,distance_str[i])
                continue
            confusion_matrix_name=pkg_list_+distance_str[i]+'.jpg'
            plot_confusion_matrix(datas_temp,config['confusion_path'],confusion_matrix_name)
            report_txt['confusion'] += ['***场景' + pkg_list_ + distance_str[i] + '混淆矩阵***  \n\n']
            image_path=os.path.join('confusion',confusion_matrix_name)
            report_txt['confusion'] += ['![图1](' + image_path + ' "Magic Gardens")  \n']
    return report_txt

def creat_table(APS_data,EEOR,MOTs,config):
    tables = {}

    # 目标级指标
    tables['target_level_indicators'] = {}
    for type in EEOR:
        for target_level_indicator in config['target_level_indicators']:
            data_temps = pd.DataFrame()
            indexs = []
            for scene in EEOR[type]:
                for index_type in config['target_level_indicators'][target_level_indicator]:
                    data_temp = pd.DataFrame(EEOR[type][scene][index_type]).T
                    data_temps = pd.concat([data_temps, data_temp])
                    for describe in EEOR[type][scene][index_type].keys():
                        index = [scene, index_type, describe]
                        indexs.append(index)
            data_temps.index = np.array(indexs).T.tolist()
            if type not in tables['target_level_indicators']:
                tables['target_level_indicators'][type] = {}
            tables['target_level_indicators'][type][target_level_indicator] = data_temps

    # 综合评价指标
    tables_temp = {}
    for type in EEOR:
        for index_type in config['sum_indexs_type']:
            if type not in tables_temp:
                tables_temp[type] = {}
            if index_type in config['indexs_type']:
                tables_temp[type][index_type] = EEOR[type]['all_scene'][index_type]['MAE']['(all)']
            elif index_type in config['APS']:
                tables_temp[type][index_type] = APS_data[type]['all_scene'][index_type]['(all)']
            elif index_type in config['MOT']:
                tables_temp[type][index_type] = MOTs[type]['all_scene'][index_type]
    tables['sum_indexs_type'] = pd.DataFrame(tables_temp)

    # AP评价指标
    tables['APS'] = {}
    for type in APS_data:
        data_temps = pd.DataFrame()
        indexs = []
        for scene in APS_data[type]:
            data_temp = pd.DataFrame(APS_data[type][scene]).T
            data_temps = pd.concat([data_temps, data_temp])
            for index_type in APS_data[type][scene]:
                index = [scene, index_type]
                indexs.append(index)
        data_temps.index = np.array(indexs).T.tolist()
        tables['APS'][type] = data_temps

    # MOT指标
    tables['MOT'] = {}
    for type in MOTs:
        tables['MOT'][type] = pd.DataFrame(MOTs[type]).T

    return tables

def creat_report(tables, report_txt_figure, object_kpi_pd, config):
    reports_txt = config['report_txt'].copy()
    count = 0
    for txt_index in reports_txt:
        count += 1
        if count == 1:
            pass
        elif count == 2:
            # 添加综合评价指标
            reports_txt[txt_index] += ['全部数据包含' + str(object_kpi_pd['scene'].unique().__len__()) + '个场景，',
                              '共' + str(object_kpi_pd['gt_type'].unique().__len__() - 1) + '类目标，',
                              '共计' + str(sum(object_kpi_pd['gt_type'] != 'NULL')) + '个目标，',
                              '成功检出' + str(sum((object_kpi_pd['gt_type'] != 'NULL') & (
                                          object_kpi_pd['pr_type'] != 'NULL'))) + '个目标。  \n\n']
            reports_txt[txt_index] += '* 场景映射  \n'
            scene_table = config['scene']
            table = pd.DataFrame(pd.DataFrame(scene_table).loc['name'])
            reports_txt[txt_index] += table.to_html(justify="center").replace('valign="top"', 'valign="middle"')
            table = tables['sum_indexs_type']
            reports_txt[txt_index] += '## 2.1 指标综述  \n'
            reports_txt[txt_index] += table.to_html(justify="center").replace('valign="top"', 'valign="middle"')
            # reports_txt[txt_index] += table.to_latex()
            reports_txt[txt_index] += ['\n\n']
            reports_txt[txt_index] += '## 2.2 数据统计情况  \n'
            reports_txt[txt_index] += report_txt_figure['data_describe']
        elif count == 3:
            # 添加目标级指标
            count3 = 0
            for type in tables['target_level_indicators']:
                count3 += 1
                reports_txt[txt_index] += '## 3.' + str(count3) + ' ' + type + '  \n'
                count31 = 0
                for target_level_indicator in tables['target_level_indicators'][type]:
                    count31 += 1
                    reports_txt[txt_index] += '### 3.' + str(count3) + '.' + str(
                        count31) + ' ' + type + target_level_indicator + '  \n'
                    table = tables['target_level_indicators'][type][target_level_indicator]
                    reports_txt[txt_index] += table.to_html(justify="center").replace('valign="top"', 'valign="middle"')
                    # reports_txt[txt_index] += table.to_latex()
                    reports_txt[txt_index] += ['\n\n']
        elif count == 4:
            # 添加AP指标
            count4 = 0
            for type in tables['APS']:
                count4 += 1
                reports_txt[txt_index] += '## 4.' + str(count4) + ' ' + type + ' AP  \n'
                table = tables['APS'][type]
                reports_txt[txt_index] += table.to_html(justify="center").replace('valign="top"', 'valign="middle"')
                # reports_txt[txt_index] += table.to_latex()
                reports_txt[txt_index] += ['\n\n']
        elif count == 5:
            # 添加AP指标
            count5 = 0
            for type in tables['MOT']:
                count5 += 1
                reports_txt[txt_index] += '## 5.' + str(count5) + ' ' + type + ' MOT  \n'
                table = tables['MOT'][type]
                reports_txt[txt_index] += table.to_html(justify="center").replace('valign="top"', 'valign="middle"')
                # reports_txt[txt_index] += table.to_latex()
                reports_txt[txt_index] += ['\n\n']
        elif count == 6:
            # 添加混淆矩阵
            reports_txt[txt_index] += report_txt_figure['confusion']
    return reports_txt